use serde::{Deserialize, Deserializer, Serialize};

fn wrap_page<'de, D>(deserializer: D) -> Result<u64, D::Error>
where
    D: Deserializer<'de>,
{
    Ok(
        match String::deserialize(deserializer)
            .ok()
            .and_then(|f| f.parse::<u64>().ok())
            .unwrap_or(1)
        {
            x if x <= 0 => 0,
            x => x,
        },
    )
}

fn wrap_page_size<'de, D>(deserializer: D) -> Result<u64, D::Error>
where
    D: Deserializer<'de>,
{
    Ok(
        match String::deserialize(deserializer)
            .map(|f| f.parse::<u64>().unwrap_or(50))
            .unwrap_or(50)
        {
            x if x <= 0 => 50,
            x if x >= 100 => 100,
            x => x,
        },
    )
}

fn default_page() -> u64 {
    1
}

fn default_page_size() -> u64 {
    50
}

#[derive(Debug, Deserialize)]
pub struct PageParams {
    #[serde(deserialize_with = "wrap_page", default = "default_page")]
    pub page: u64,
    #[serde(deserialize_with = "wrap_page_size", default = "default_page_size")]
    pub page_size: u64,
    #[serde(default)]
    pub zero_index: bool,
}

impl Default for PageParams {
    fn default() -> Self {
        Self {
            page: 1,
            page_size: 50,
            zero_index: false,
        }
    }
}

impl PageParams {
    pub fn limit(&self) -> u64 {
        self.page_size
    }

    pub fn offset(&self) -> u64 {
        self.page_size * self.zero_page()
    }

    /// 返回以0索引开始的page的值
    pub fn zero_page(&self) -> u64 {
        if self.zero_index {
            self.page
        } else {
            self.page - 1
        }
    }
}

#[derive(Debug, Serialize)]
pub struct PageInfo<T>
where
    T: Serialize,
{
    #[serde(deserialize_with = "wrap_page", default = "default_page")]
    page: u64,
    #[serde(deserialize_with = "wrap_page_size", default = "default_page_size")]
    page_size: u64,
    total_pages: u64,
    total_items: u64,
    pub datas: Vec<T>,
}

impl<T> PageInfo<T>
where
    T: Serialize,
{
    pub fn new(datas: Vec<T>, page: &PageParams, total_items: u64) -> Self {
        let total_pages = total_items / page.page_size
            + if total_items % page.page_size == 0 {
                0
            } else {
                1
            };
        Self {
            page: page.page,
            page_size: page.page_size,
            total_pages,
            total_items,
            datas,
        }
    }
}
